/**
 * Trabalho 2 
 * OpenMP 1 - Texto wikipedia.txt 
 *
 * GRUPO 9 : Alfredo L. Porfirio
 *           Diego Gomes da Silva
 *           Marcos V. S. Ferreira
 *
 * Compile: g++ -o omp1 omp1.cpp -fopenmp
 */

// Bibliotecas
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include <omp.h>

// Valor maximo pra se calcular o numero primo
#define NMAX 100000

// Ambiente
using namespace std;

// Vetor para calculo dos numeros pi até 10000 
int v[NMAX];


// contadores de palindromos e de primos
int n_pali = 0;
int n_prime = 0;


// deixa as letras todas maisculas
string toUpperCase(string s) {

  for (int i = 0; i < s.size(); i++)
    s[i]=toupper(s[i]);
  return s;
}

// Faz a tabela de primos pelo Crivo Erastótenes (calcula todos os primos de uma vez só)
int table_prime() {
    int i, j;
    int raiz , indx;
 
    raiz=(int)sqrt(NMAX);
    
    #pragma omp parallel default(none) shared(v) private(i) firstprivate(raiz,indx) num_threads(2)
    for (i=2 + omp_get_thread_num()*NMAX/2; i<NMAX/2 + omp_get_thread_num()*NMAX/2; i++) v[i]=i;
    
    for (i=2 + omp_get_thread_num()*raiz/2; i<raiz/2 + omp_get_thread_num()*raiz/2; i++) {
        if (v[i]==i) {
            for (j=i+i; j<NMAX; j+=i) {
                v[j]=0; // removendo da lista
            }
        }
    }
}
 
// Acessa a tabela para verificar se é primo
bool is_prime(int value) {
    return (bool)v[value];
}


// Limpa a string de apontuacao
void remove_pontuacion(string& in)
{
    for(int i=0; i <in.length(); i++)
    {
        // ispunct é uma funcao da ctype.h que detecta se é alguma potuacao ja predefinida
        if(ispunct(in[i]))
        	in[i]= ' ';
    }
    return;
}

// Soma os valores char da tabela ASCII
int sum_char(string& word) {
    int i, out = 0;
    for(i=0; i<word.size(); i++) out += word[i];
    return out;
}


// verifica se um string é palindromo, utilizando-se de operators
bool palindrome(string s) {
  if( equal(s.begin(), s.begin() + s.size()/2, s.rbegin()) )
      return true;
  else
      return false;
}

// Dado uma linha do arquivo (in), quebra em palavras (palavras entre espacos brancos)
void token(string& in) {
    istringstream iss(in);

    do
    {
        string sub;
        iss >> sub;
        if(sub.size() > 2) {
          sub = toUpperCase(sub);
          if(palindrome(sub)) {
              n_pali++;
              //cout << sub << endl;
              int sum = sum_char(sub);
              if(is_prime(sum)) 
                n_prime++;
          } 
        }
    } while (iss);

    iss.clear();
    
}

// Varivel para ajudar contar o tempo
#define MICRO_PER_SECOND 1000000 



// Funcao principal
int main() {
  // Variaveis auxiliares para contar o tempo
  struct timeval start_time;
  struct timeval stop_time;
  float time_clivo, time_texto;

  // funcao mais precisa pra contar o tempo da <sys/time.h>
  // ponto de partida
  gettimeofday( &start_time, NULL );
    table_prime(); 
  gettimeofday( &stop_time, NULL );
  time_clivo = (float)(stop_time.tv_sec - start_time.tv_sec);
  time_clivo += (stop_time.tv_usec - start_time.tv_usec)/(float)MICRO_PER_SECOND; 
  
  

   

    gettimeofday( &start_time, NULL ); // fecha o tempo de calcular o primos
    ifstream fin ("wikipedia.txt"); // ifstream = padrão ios:in
    if (fin.is_open())
    {
         

      // Cria-se um regiao parelela considerando as variaveis shared e reduction
      // a ideia eh parelelizar o a busca de palindromos por linha do texto
      // o numero de processos paralelos depende da arquitetura do computador
      #pragma omp parallel default(none) shared(fin,cout)  reduction(+:n_pali,n_prime)
      {
        // neste caso, os contadores sao variaveis locais
        // para depois ser mostrado o servico de cada thread 
        int local_n_pali = 0;
        int local_n_prime = 0; 

          while ( !fin.eof() ) //enquanto end of file for false continua
          {
              string line;
              // regiao critica para acessar o arquivo e pegar uma linha
              #pragma omp critical 
              {
                 getline(fin, line, '\n'); // pega uma linha do texto ate o '\r'
              } 
              // todo processo igual o sequencial
              remove_pontuacion(line); // remove pontuacao
              istringstream iss(line);
              do
              {
                string sub;
                iss >> sub;
                if(sub.size() > 2) {
                  sub = toUpperCase(sub);
                  if(palindrome(sub)) {
                    n_pali++;local_n_pali++;
                    //cout << sub << endl;
                    int sum = sum_char(sub);
                      if(is_prime(sum)) {
                        n_prime++;local_n_prime++;
                      }
                    } 
                  }
              } while (iss);

              iss.clear();
              line.clear();
          }


          // mostrar o numero de processos q cada thread executou  
          #pragma omp critical 
          {
            cout << "Thread "<< omp_get_thread_num() << " resultado: " << "numero de palavras palindromas: " << local_n_pali << endl;
            cout << "Thread "<< omp_get_thread_num() << " resultado: " << "numero de palavras palindromas: " << local_n_prime << endl;
          }
      }
      fin.close();


      gettimeofday( &stop_time, NULL );
      time_texto = (float)(stop_time.tv_sec - start_time.tv_sec);
      time_texto += (stop_time.tv_usec - start_time.tv_usec)/(float)MICRO_PER_SECOND; 
       
    }
    else cout << "nao foi possivel abrir o arquivo.." << endl; // mensagem de erro
    
    
    // imprime resultados
    cout << "Totla de palavras palindromas: " << n_pali << endl;
    cout << "Total de primos: " << n_prime << endl;
    cout << "Tempo de Processamento da Crivo de Erastótenes: " << time_clivo << "s" << endl;
    cout << "Tempo de Processamento do Texto: " << time_texto << " s" << endl;
    cout << "Tempo de Processamento total: " << time_texto + time_clivo << " s" << endl;


    return 0;
}